Skip to content

Upgrade avatarMiddleware to polymiddleware#5779

Open
compulim wants to merge 56 commits intomainfrom
agent-polymiddleware-promoter
Open

Upgrade avatarMiddleware to polymiddleware#5779
compulim wants to merge 56 commits intomainfrom
agent-polymiddleware-promoter

Conversation

@compulim
Copy link
Contributor

@compulim compulim commented Mar 19, 2026

Changelog Entry

  • 💥 avatarMiddleware is being deprecated in favor of polymiddleware. It will be removed on or after 2028-03-18, related to PR #5779

Added

Removed

  • avatarMiddleware is being deprecated in favor of polymiddleware. It will be removed on or after 2028-03-18, related to PR #5779

Fixed

  • Fixed <AddFullBundle> should not re-render when attachment[ForScreenReader]Middleware is updated without noticeable different (iterateEquals), by @compulim, in PR #5779

Description

Upgraded avatar middleware to avatarPolymiddleware.

Unified and memorized the singleToArray(middleware ?? []) pattern.

Also fixed wasted rendering in <AddFullBundle>.

Also fixed test harness legacy <RunHook> that is being rendered multiple times:

  • Parent component is being re-rendered, but <RunHook> is not memorized
  • Code inside <RunHook> subscribes to context which is changed, then the render loop is re-executed

Design

Why render legacy middleware before polymiddleware?

  • Legacy middleware should have high priority than defaults
  • Default middleware will be upgraded to polymiddleware, however, they should have lower priority
  • Default middleware are implemented in the component package, and passed via polymiddleware props
    • They are UI, cannot be implemented in api package

We have a few way out, either one of the followings:

  • Add a new lowPriorityPolymiddleware props for default polymiddleware, so we can put them after legacy
    • We don't want any special treatments or any prioritization system
  • We put the upgrade logics inside both api and component package
    • component will upgrade legacy to polymiddleware and prioritize properly
    • Spaghetti code and it is difficult to test the logic in api package
  • We always render legacy middleware before polymiddleware
    • Default middleware are polymiddleware, has lower priority than legacy

The simplest and logical move is #3: render legacy middleware before polymiddleware.

Specific Changes

  • Moved some helper functions to base and react-hooks packages
  • Added new useMemoIterable to help memorizing iterable results
  • Added debug data in the enhancer of polymiddleware
  • Fixed test harness that RunHookActivity could re-render and running hooks multiple times
    • For example, if style options changed, some useXXX hooks will be invalidated and causing the component to be re-rendered
  • I have added tests and executed them locally
  • I have updated CHANGELOG.md
  • I have updated documentation

Review Checklist

This section is for contributors to review your work.

  • Accessibility reviewed (tab order, content readability, alt text, color contrast)
  • Browser and platform compatibilities reviewed
  • CSS styles reviewed (minimal rules, no z-index)
  • Documents reviewed (docs, samples, live demo)
  • Internationalization reviewed (strings, unit formatting)
  • package.json and package-lock.json reviewed
  • Security reviewed (no data URIs, check for nonce leak)
  • Tests reviewed (coverage, legitimacy)

@compulim compulim marked this pull request as ready for review March 25, 2026 21:25
Copilot AI review requested due to automatic review settings March 25, 2026 21:25
@compulim compulim changed the title [WIP] Upgrade avatarMiddleware to polymiddleware Upgrade avatarMiddleware to polymiddleware Mar 25, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR continues the migration from legacy avatarMiddleware toward the newer polymiddleware system by introducing avatar polymiddleware support (including legacy bridging), updating the default UI middleware wiring to prioritize legacy middleware ahead of polymiddleware, and reducing unnecessary re-renders via iterable memoization utilities.

Changes:

  • Added avatar polymiddleware support across api, api-middleware, component, and bundle, including legacy-to-polymiddleware bridging and new avatar middleware tests/docs.
  • Introduced iterateEquals + useMemoIterable to memoize iterable results and reduce wasted re-renders (e.g., <AddFullBundle>, middleware array patching, polymiddleware composer extraction).
  • Moved/centralized singleToArray + OneOrMany into @msinternal/botframework-webchat-base/utils and updated references.

Reviewed changes

Copilot reviewed 73 out of 92 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
packages/test/page-object/src/globals/testHelpers/createRunHookActivityMiddleware.js Test harness: track/guard repeated hook execution during re-renders.
packages/test/page-object/src/globals/testHelpers/createDirectLineWithTranscript.js Test helper: add optional activity patching hook for transcripts.
packages/test/page-object/src/globals/testHelpers/activityGrouping/ActivityGroupingSurface.js Test helper: switch activity mutation from middleware to transcript patching.
packages/react-hooks/src/useMemoIterable.ts New hook to memoize iterables via structural equality.
packages/react-hooks/src/useDebugDeps.ts New debug deps hook moved into shared react-hooks package.
packages/react-hooks/src/index.ts Export new hooks from react-hooks package.
packages/react-hooks/package.json Add local/dev dependency wiring for base utils.
packages/core/src/utils/singleToArray.ts Remove core-local singleToArray (moved to base utils).
packages/core/src/types/OneOrMany.ts Remove core-local OneOrMany (moved to base utils).
packages/core/src/index.ts Re-export deprecated singleToArray/OneOrMany from base utils; tidy imports.
packages/core-debug-api/src/RestrictedDebugAPI.ts ESLint disable cleanup for empty breakpoint function.
packages/component/src/index.ts Point createCoreActivityMiddleware export to updated activity polymiddleware module.
packages/component/src/boot/internal.ts Export internal avatar-styleOptions symbol type for portability.
packages/component/src/Utils/singleToArray.ts Remove component-local singleToArray (moved to base utils).
packages/component/src/Transcript/hooks/useRenderActivityProps.ts Use avatar polymiddleware render callback instead of legacy avatar renderer hook.
packages/component/src/Middleware/Avatar/createDefaultAvatarPolymiddleware.tsx New default avatar polymiddleware factory driven by style options.
packages/component/src/Middleware/Avatar/DefaultAvatar.tsx Convert default avatar from legacy middleware factory to memoized component export.
packages/component/src/Middleware/Activity/defaultActivityPolymiddleware.tsx Bridge default legacy activity middleware into a polymiddleware (and keep compat export).
packages/component/src/Composer.tsx Memoize middleware arrays; wire default activity/avatar via polymiddleware (lower priority than legacy).
packages/component/src/BasicTranscript.tsx Memoize internal transcript component to reduce wasted renders.
packages/component/src/Activity/Avatar.tsx Update imports to new DefaultAvatar component export.
packages/bundle/src/useComposerProps.ts Allow optional attachment middleware arrays; avoid spreading undefined.
packages/bundle/src/boot/actual/middleware.ts Export avatar polymiddleware APIs from bundle middleware entry.
packages/bundle/src/AddFullBundle.tsx Use useMemoIterable to avoid rerenders when attachment middleware values are equivalent.
packages/bundle/package.json Add dependency on internal react-hooks package.
packages/base/src/utils/singleToArray.ts New shared deprecated singleToArray in base utils.
packages/base/src/utils/iterateEquals.ts New iterable structural-equality helper.
packages/base/src/utils/iterateEquals.spec.ts Unit tests for iterateEquals.
packages/base/src/utils/index.ts Export new base utils (iterateEquals, OneOrMany, singleToArray).
packages/base/src/utils/OneOrMany.ts New shared deprecated OneOrMany type in base utils.
packages/api/src/types/AvatarMiddleware.ts Extend legacy avatar middleware request typing to preserve original polymiddleware request.
packages/api/src/providers/GroupActivities/GroupActivitiesComposer.tsx Allow groupActivitiesMiddleware to be optional.
packages/api/src/middleware/useBuildRenderAvatarCallback.ts API-layer patch to add styleOptions into internal avatar request (bridging constraint).
packages/api/src/middleware/AvatarPolymiddlewareProxy.tsx API-layer proxy wrapper that injects style options into raw proxy.
packages/api/src/legacy/createAvatarPolymiddlewareFromLegacy.tsx New legacy avatar middleware bridge into avatar polymiddleware.
packages/api/src/legacy/createActivityPolymiddlewareFromLegacy.tsx Add debug metadata to activity polymiddleware bridged from legacy middleware.
packages/api/src/legacy/LegacyActivityBridge.tsx Add displayName for legacy activity bridge component.
packages/api/src/hooks/useCreateAvatarRenderer.ts Deprecate legacy avatar renderer hook; implement via avatar polymiddleware callback.
packages/api/src/hooks/internal/useDebugDeps.js Remove old API-local debug deps hook (moved to react-hooks package).
packages/api/src/hooks/internal/WebChatAPIContext.ts Remove legacy avatarRenderer from API context typing.
packages/api/src/hooks/Composer.tsx Prioritize legacy middleware before polymiddleware; bridge legacy avatar middleware into polymiddleware.
packages/api/src/boot/middleware.ts Export avatar polymiddleware APIs and legacy bridge factory from API boot middleware entry.
packages/api/src/boot/internal.ts Export internal avatar styleOptions symbol from api-middleware.
packages/api-middleware/src/private/templatePolymiddleware.tsx Attach debug metadata (name/enhancer) to templated polymiddleware factories.
packages/api-middleware/src/legacy/avatarMiddleware.ts Introduce legacy avatar middleware typings/symbol for bridging.
packages/api-middleware/src/legacy.ts Export legacy avatar middleware types from api-middleware.
packages/api-middleware/src/index.ts Export avatar polymiddleware APIs and legacy avatar symbol from api-middleware.
packages/api-middleware/src/avatarPolymiddleware.tsx Add avatar polymiddleware implementation (provider/proxy/extract/build-render).
packages/api-middleware/src/PolymiddlewareComposer.tsx Add avatar polymiddleware provider to composer; switch memoization to useMemoIterable.
packages/api-middleware/package.json Adjust watch inputs for notify-build script.
package-lock.json Lockfile updates for new internal package dependency wiring.
docs/MIDDLEWARE.md Document avatar polymiddleware support and update priority rules.
tests/html2/transcript/legacyActivityMiddleware.reactionButtons.html Refactor legacy activity middleware test for clearer wrapping logic.
tests/html2/timestamp/attachmentSendTimeout.html Guard against repeated execution via updated RunHook helper.
tests/html2/scrollToEndButton/scrollToEndButton.persistWhileCallingUseScrollTo.html Modernize test harness usage; wait-for assertion stabilization.
tests/html2/middleware/avatar/useBuildRenderAvatarCallback.html.snap-1.png New snapshot for avatar build-render-callback test.
tests/html2/middleware/avatar/useBuildRenderAvatarCallback.html New test verifying useBuildRenderAvatarCallback behavior with legacy + polymiddleware.
tests/html2/middleware/avatar/renderProxy.html.snap-1.png New snapshot for avatar proxy rendering test.
tests/html2/middleware/avatar/renderProxy.html New test verifying <AvatarPolymiddlewareProxy> behavior with legacy + polymiddleware.
tests/html2/middleware/avatar/polymiddleware/delete.html New test: polymiddleware deletes avatar for specific activities.
tests/html2/middleware/avatar/polymiddleware/defaultAvatar.styleOptions.html.snap-2.png Snapshot for default avatar behavior after styleOptions change.
tests/html2/middleware/avatar/polymiddleware/defaultAvatar.styleOptions.html.snap-1.png Snapshot for default avatar behavior baseline.
tests/html2/middleware/avatar/polymiddleware/defaultAvatar.styleOptions.html New test: default avatar responds to styleOptions changes.
tests/html2/middleware/avatar/polymiddleware/decorate.html New test: polymiddleware decorates avatar output.
tests/html2/middleware/avatar/polymiddleware/changing.html New test: toggling polymiddleware on/off at runtime.
tests/html2/middleware/avatar/polymiddleware/addOrReplace.html New test: polymiddleware adds/replaces avatar output for certain roles.
tests/html2/middleware/avatar/legacyAvatarMiddleware/styleOptions.html New test: legacy avatar middleware receives styleOptions and reacts to changes.
tests/html2/middleware/avatar/legacyAvatarMiddleware/delete.html New test: legacy avatar middleware deletes avatar for certain roles.
tests/html2/middleware/avatar/legacyAvatarMiddleware/decorate.html New test: legacy avatar middleware decorates avatar output.
tests/html2/middleware/avatar/legacyAvatarMiddleware/changing.html New test: toggling legacy avatar middleware on/off at runtime.
tests/html2/middleware/avatar/legacyAvatarMiddleware/addOrReplace.html New test: legacy avatar middleware adds/replaces avatar output.
tests/html2/middleware/avatar/backwardCompatibility.html New test: legacy avatar middleware priority over polymiddleware for compatibility.
tests/html2/activityGrouping/activityGrouping.legacyActivityMiddleware.skip.html Mark failing legacy activity middleware test with explanatory skip rationale.
CHANGELOG.md Changelog entries for avatarMiddleware deprecation + polymiddleware support + rerender fix.
AGENTS.md Update contributor guidance (immutability, validation, tooling/testing conventions).
.github/workflows/pull-request-validation.yml Update CI runner matrix (macOS label change).
.github/agents/polymiddleware-promoter.agent.md Add agent guidance for upgrading middleware to polymiddleware.
.eslintrc.yml Broaden unused-args ignore pattern for underscore-prefixed args.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants